using System;
using System.Collections;
using System.Drawing;
using System.Globalization;
using System.Windows.Forms;
using Xmef.Interfaces;
using Xmef.Library;

namespace Xmef.Controls
{
    public partial class PluginManagerForm : Form, IPluginManagerContainer
    {
        private readonly ListViewItemComparer _comparer = new ListViewItemComparer(0, SortOrder.Ascending);
        
        private readonly Hashtable _groups = new Hashtable();
        
        private readonly Hashtable _items = new Hashtable();

        private static PluginManagerForm _pluginManagerForm;

        private event EventHandler<PluginEventArgs> _pluginActivated;
        
        private event EventHandler<PluginEventArgs> _pluginDeactivated;

        public event EventHandler<PluginEventArgs> PluginActivated
        {
            add
            {
                if (!PluginHelper.GetInvocationList(this._pluginActivated, value))
                {
                    this._pluginActivated = (EventHandler<PluginEventArgs>) Delegate.Combine(this._pluginActivated, value);
                }
            }
            remove
            {
                if (PluginHelper.GetInvocationList(this._pluginActivated, value))
                {
                    this._pluginActivated = (EventHandler<PluginEventArgs>)Delegate.Remove(this._pluginActivated, value);
                }
            }
        }

        public event EventHandler<PluginEventArgs> PluginDeactivated
        {
            add
            {
                if (!PluginHelper.GetInvocationList(this._pluginDeactivated, value))
                {
                    this._pluginDeactivated = (EventHandler<PluginEventArgs>)Delegate.Combine(this._pluginDeactivated, value);
                }
            }
            remove
            {
                if (PluginHelper.GetInvocationList(this._pluginDeactivated, value))
                {
                    this._pluginDeactivated = (EventHandler<PluginEventArgs>)Delegate.Remove(this._pluginDeactivated, value);
                }
            }
        }
       
        static PluginManagerForm()
        {
            _pluginManagerForm = new PluginManagerForm();
        }

        protected PluginManagerForm()
        {
            InitializeGraphics();
            InitializeComponent();
            
            pluginsListView.Sorting = SortOrder.Ascending;
            pluginsListView.ListViewItemSorter = _comparer;
        }

        public static void ShowWindow(IWin32Window owner)
        {
            if (!_pluginManagerForm.Visible)
            {
                _pluginManagerForm.Show(owner);
            }
            else
            {
                _pluginManagerForm.Focus();
            }
        }

        private void InitializeGraphics()
        {
            SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true);
        }

        private void pluginsListView_ColumnClick(object sender, ColumnClickEventArgs e)
        {
            pluginsListView.BeginUpdate();
            if (_comparer.ColumnIndex == e.Column)
            {
                switch (_comparer.SortOrder)
                {
                    case SortOrder.Ascending:
                        _comparer.SortOrder = SortOrder.Descending;
                        break;
                    case SortOrder.Descending:
                        _comparer.SortOrder = SortOrder.Ascending;
                        break;
                }
            }
            else
            {
                _comparer.SortOrder = SortOrder.Ascending;
                _comparer.ColumnIndex = e.Column;
            }
            pluginsListView.Sort();
            pluginsListView.EndUpdate();
        }
        public static IPluginManagerContainer Instance 
        {
            get
            {
                return _pluginManagerForm;
            }
        }
        public void RegisterHost(IPluginHost host)
        {
            LoadGroup(host);
            BeginUpdate();
            foreach (IPluginObject obj in host.LoadedPlugins)
            {
                LoadObject(obj);
            }
            EndUpdate();
            ControlHelper.Action<ListView>(pluginsListView, listView => listView.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent));
            IPluginHostContainer container = host as IPluginHostContainer;
            if (container != null)
            {
                container.PluginLoad += host_PluginLoad;
                container.PluginUnload += host_PluginUnload;
            }
            ControlHelper.Action<ListView>(pluginsListView, listView => listView.Update());
        }
        public void UnregisterHost(IPluginHost host)
        {
            IPluginHostContainer container = host as IPluginHostContainer;
            if (container != null)
            {
                container.PluginLoad -= host_PluginLoad;
                container.PluginUnload -= host_PluginUnload;
            }
            BeginUpdate();
            foreach (IPluginObject obj in host.LoadedPlugins)
            {
                UnloadObject(obj);
            }
            EndUpdate();
            UnloadGroup(host);
            ControlHelper.Action<ListView>(pluginsListView, listView => listView.Update());
        }
        private void LoadGroup(IPluginHost host)
        {
            if (!_groups.ContainsKey(host.Id))
            {
                var group = new ListViewGroup(host.Id.ToString(), string.Format(CultureInfo.InvariantCulture, "{{{0}}}", host.HostName));
                LogHelper.Write(string.Format(CultureInfo.InvariantCulture, "register group={0}", host.HostName));
                _groups.Add(host.Id, group);
                ControlHelper.Action<ListView>(pluginsListView, listView => listView.Groups.Add(group));
            }
        }
        private void UnloadGroup(IPluginHost host)
        {
            if (_groups.ContainsKey(host.Id))
            {
                var group = (ListViewGroup)_groups[host.Id];
                LogHelper.Write(string.Format(CultureInfo.InvariantCulture, "unregister group={0}", host.HostName));
                _groups.Remove(host.Id);
                ControlHelper.Action<ListView>(pluginsListView, listView => listView.Groups.Remove(group));
            }
        }
        private void host_PluginLoad(object sender, PluginHostEventArgs e)
        {
            BeginUpdate();
            LoadObject(e.Plugin);
            EndUpdate();
        }
        private void host_PluginUnload(object sender, PluginHostEventArgs e)
        {
            BeginUpdate();
            UnloadObject(e.Plugin);
            EndUpdate();
        }
        private ListViewItem CreateListViewItem(string text)
        {
            ListViewItem item = null;
            ControlHelper.Action<PluginManagerForm>(this, control => item = new ListViewItem(text));
            return item;
        }
        private void LoadObject(IPluginObject plugin)
        {
            if (!_items.ContainsKey(plugin.Id))
            {
                LogHelper.Write(string.Format(CultureInfo.InvariantCulture, "register item={0}", plugin));
                var item = CreateListViewItem(plugin.Attributes.Name);
                item.SubItems.Add(plugin.Attributes.Author);
                item.SubItems.Add(plugin.Attributes.Description);
                item.ToolTipText = plugin.Attributes.Description;
                item.Group = (ListViewGroup)_groups[plugin.Host.Id];
                item.Tag = plugin;
                item.Checked = plugin.IsActive;
                _items.Add(plugin.Id, item);
                ControlHelper.Action<ListView>(pluginsListView, listView => listView.Items.Add(item));
            }
        }
        private void UnloadObject(IPluginObject plugin)
        {
            if (_items.ContainsKey(plugin.Id))
            {
                LogHelper.Write(string.Format(CultureInfo.InvariantCulture, "unregister item={0}", plugin));
                var item = (ListViewItem)_items[plugin.Id];
                _items.Remove(plugin.Id);
                ControlHelper.Action<ListView>(pluginsListView, listView => listView.Items.Remove(item));
            }
        }
        private void BeginUpdate()
        {
            pluginsListView.ItemCheck -= listView1_ItemCheck;
            ControlHelper.Action<ListView>(pluginsListView, listView => listView.BeginUpdate());
        }
        private void EndUpdate()
        {
            pluginsListView.ItemCheck += listView1_ItemCheck;
            ControlHelper.Action<ListView>(pluginsListView, listView => listView.EndUpdate());
        }
        private void tabControl1_DrawItem(object sender, DrawItemEventArgs e)
        {
            TabPage page = pluginsTabControl.TabPages[e.Index];
            Graphics g = e.Graphics;
            g.FillRectangle(new SolidBrush(page.BackColor), e.Bounds);
            g.DrawString(page.Text, page.Font, new SolidBrush(page.ForeColor), e.Bounds, StringFormat.GenericDefault);
        }
        private void listView1_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            ListViewItem item = pluginsListView.Items[e.Index];
            if (item != null)
            {
                var obj = (IPluginObject)item.Tag;
                if (e.NewValue == CheckState.Checked)
                {
                    if (!obj.IsActive)
                    {
                        LogHelper.Write(string.Format(CultureInfo.InvariantCulture, "activate item={0}", obj));
                        if (obj.Activate())
                        {
                            OnPluginActivated(obj);
                        }
                    }
                    return;
                }
                if (e.NewValue == CheckState.Unchecked)
                {
                    if (obj.IsActive)
                    {
                        LogHelper.Write(string.Format(CultureInfo.InvariantCulture, "deactivate item={0}", obj));
                        if (obj.Deactivate())
                        {
                            OnPluginDeactivated(obj);
                        }
                    }
                    return;
                }
            }
        }

        protected void OnPluginActivated(IPluginObject obj)
        {
            if (_pluginActivated != null && obj != null)
            {
                _pluginActivated(this, new PluginEventArgs(obj));
            }
        }
        protected void OnPluginDeactivated(IPluginObject obj)
        {
            if (_pluginDeactivated != null && obj != null)
            {
                _pluginDeactivated(this, new PluginEventArgs(obj));
            }
        }
        private void SudokuOptions_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (e.CloseReason == CloseReason.UserClosing)
            {
                e.Cancel = true;
                this.Hide();
            }
        }
        private interface IListViewItemComparer : IComparer
        {
            int ColumnIndex { get; set; }
            SortOrder SortOrder { get; set; }
        }
        private class ListViewItemComparer : IListViewItemComparer
        {
            private int _columnIndex;
            private SortOrder _sortOrder;
            public ListViewItemComparer(int column, SortOrder order)
            {
                _columnIndex = column;
                _sortOrder = order;
            }
            public int ColumnIndex { get { return _columnIndex; } set { _columnIndex = value; } }
            public SortOrder SortOrder { get { return _sortOrder; } set { _sortOrder = value; } }
            public int Compare(object x, object y)
            {
                switch (_sortOrder)
                {
                    case SortOrder.Ascending:
                        return string.Compare(((ListViewItem)x).SubItems[_columnIndex].Text, ((ListViewItem)y).SubItems[_columnIndex].Text);
                    case SortOrder.Descending:
                        return string.Compare(((ListViewItem)y).SubItems[_columnIndex].Text, ((ListViewItem)x).SubItems[_columnIndex].Text);
                    default:
                        return 0;
                }
            }
        }
    }
}